// Copyright Epic Games, Inc. All Rights Reserved.

#include "LyraTeamCreationComponent.h"
#include "GameModes/LyraExperienceManagerComponent.h"
#include "LyraTeamPublicInfo.h"
#include "LyraTeamPrivateInfo.h"
#include "Player/LyraPlayerState.h"
#include "Engine/World.h"
#include "GameModes/LyraGameMode.h"

#if WITH_EDITOR
#include "Misc/DataValidation.h"
#endif

#include UE_INLINE_GENERATED_CPP_BY_NAME(LyraTeamCreationComponent)

/**
 * 构造函数
 */
ULyraTeamCreationComponent::ULyraTeamCreationComponent(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	PublicTeamInfoClass = ALyraTeamPublicInfo::StaticClass(); // 设置公共团队信息类
	PrivateTeamInfoClass = ALyraTeamPrivateInfo::StaticClass(); // 设置私有团队信息类
}

#if WITH_EDITOR
/**
 * 数据验证
 */
EDataValidationResult ULyraTeamCreationComponent::IsDataValid(FDataValidationContext& Context) const
{
	EDataValidationResult Result = Super::IsDataValid(Context); // 调用父类数据验证

	//@TODO: TEAMS: 验证所有显示资源是否设置了相同的属性！

	return Result; // 返回验证结果
}
#endif

/**
 * 开始播放
 */
void ULyraTeamCreationComponent::BeginPlay()
{
	Super::BeginPlay(); // 调用父类开始播放

	// 监听经验加载完成
	AGameStateBase* GameState = GetGameStateChecked<AGameStateBase>(); // 获取游戏状态
	ULyraExperienceManagerComponent* ExperienceComponent = GameState->FindComponentByClass<ULyraExperienceManagerComponent>(); // 查找经验管理器组件
	check(ExperienceComponent); // 检查经验组件有效性
	ExperienceComponent->CallOrRegister_OnExperienceLoaded_HighPriority(FOnLyraExperienceLoaded::FDelegate::CreateUObject(this, &ThisClass::OnExperienceLoaded)); // 注册经验加载回调
}

/**
 * 当经验加载完成时调用
 */
void ULyraTeamCreationComponent::OnExperienceLoaded(const ULyraExperienceDefinition* Experience)
{
#if WITH_SERVER_CODE
	if (HasAuthority()) // 如果有权限
	{
		ServerCreateTeams(); // 服务器创建团队
		ServerAssignPlayersToTeams(); // 服务器分配玩家到团队
	}
#endif
}

#if WITH_SERVER_CODE

/**
 * 服务器端创建团队
 */
void ULyraTeamCreationComponent::ServerCreateTeams()
{
	for (const auto& KVP : TeamsToCreate) // 遍历要创建的团队
	{
		const int32 TeamId = KVP.Key; // 获取团队ID
		ServerCreateTeam(TeamId, KVP.Value); // 服务器创建团队
	}
}

/**
 * 服务器端分配玩家到团队
 */
void ULyraTeamCreationComponent::ServerAssignPlayersToTeams()
{
	// 分配已存在的玩家到团队
	AGameStateBase* GameState = GetGameStateChecked<AGameStateBase>(); // 获取游戏状态
	for (APlayerState* PS : GameState->PlayerArray) // 遍历玩家数组
	{
		if (ALyraPlayerState* LyraPS = Cast<ALyraPlayerState>(PS)) // 如果是Lyra玩家状态
		{
			ServerChooseTeamForPlayer(LyraPS); // 服务器为玩家选择团队
		}
	}

	// 监听新玩家登录
	ALyraGameMode* GameMode = Cast<ALyraGameMode>(GameState->AuthorityGameMode); // 获取游戏模式
	check(GameMode); // 检查游戏模式有效性

	GameMode->OnGameModePlayerInitialized.AddUObject(this, &ThisClass::OnPlayerInitialized); // 添加玩家初始化监听
}

/**
 * 为玩家选择团队
 */
void ULyraTeamCreationComponent::ServerChooseTeamForPlayer(ALyraPlayerState* PS)
{
	if (PS->IsOnlyASpectator()) // 如果只是观战者
	{
		PS->SetGenericTeamId(FGenericTeamId::NoTeam); // 设置为无团队
	}
	else
	{
		const FGenericTeamId TeamID = IntegerToGenericTeamId(GetLeastPopulatedTeamID()); // 获取人口最少的团队ID
		PS->SetGenericTeamId(TeamID); // 设置团队ID
	}
}

/**
 * 当玩家初始化时调用
 */
void ULyraTeamCreationComponent::OnPlayerInitialized(AGameModeBase* GameMode, AController* NewPlayer)
{
	check(NewPlayer); // 检查新玩家有效性
	check(NewPlayer->PlayerState); // 检查玩家状态有效性
	if (ALyraPlayerState* LyraPS = Cast<ALyraPlayerState>(NewPlayer->PlayerState)) // 如果是Lyra玩家状态
	{
		ServerChooseTeamForPlayer(LyraPS); // 服务器为玩家选择团队
	}
}

/**
 * 服务器端创建团队
 */
void ULyraTeamCreationComponent::ServerCreateTeam(int32 TeamId, ULyraTeamDisplayAsset* DisplayAsset)
{
	check(HasAuthority()); // 检查是否有权限

	//@TODO: 确保团队不存在

	UWorld* World = GetWorld(); // 获取世界
	check(World); // 检查世界有效性

	FActorSpawnParameters SpawnInfo; // 创建生成参数
	SpawnInfo.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; // 设置碰撞处理方式

	ALyraTeamPublicInfo* NewTeamPublicInfo = World->SpawnActor<ALyraTeamPublicInfo>(PublicTeamInfoClass, SpawnInfo); // 生成公共团队信息
	checkf(NewTeamPublicInfo != nullptr, TEXT("Failed to create public team actor from class %s"), *GetPathNameSafe(*PublicTeamInfoClass)); // 检查生成是否成功
	NewTeamPublicInfo->SetTeamId(TeamId); // 设置团队ID
	NewTeamPublicInfo->SetTeamDisplayAsset(DisplayAsset); // 设置团队显示资源

	ALyraTeamPrivateInfo* NewTeamPrivateInfo = World->SpawnActor<ALyraTeamPrivateInfo>(PrivateTeamInfoClass, SpawnInfo); // 生成私有团队信息
	checkf(NewTeamPrivateInfo != nullptr, TEXT("Failed to create private team actor from class %s"), *GetPathNameSafe(*PrivateTeamInfoClass)); // 检查生成是否成功
	NewTeamPrivateInfo->SetTeamId(TeamId); // 设置团队ID
}

/**
 * 获取人口最少的团队ID
 */
int32 ULyraTeamCreationComponent::GetLeastPopulatedTeamID() const
{
	const int32 NumTeams = TeamsToCreate.Num(); // 获取团队数量
	if (NumTeams > 0) // 如果有团队
	{
		TMap<int32, uint32> TeamMemberCounts; // 团队成员计数映射
		TeamMemberCounts.Reserve(NumTeams); // 预留空间

		for (const auto& KVP : TeamsToCreate) // 遍历要创建的团队
		{
			const int32 TeamId = KVP.Key; // 获取团队ID
			TeamMemberCounts.Add(TeamId, 0); // 添加团队ID到计数映射
		}

		AGameStateBase* GameState = GetGameStateChecked<AGameStateBase>(); // 获取游戏状态
		for (APlayerState* PS : GameState->PlayerArray) // 遍历玩家数组
		{
			if (ALyraPlayerState* LyraPS = Cast<ALyraPlayerState>(PS)) // 如果是Lyra玩家状态
			{
				const int32 PlayerTeamID = LyraPS->GetTeamId(); // 获取玩家团队ID

				if ((PlayerTeamID != INDEX_NONE) && !LyraPS->IsInactive())	// 不计算未分配或断开连接的玩家
				{
					check(TeamMemberCounts.Contains(PlayerTeamID)) // 检查团队ID是否存在
					TeamMemberCounts[PlayerTeamID] += 1; // 增加团队成员计数
				}
			}
		}

		// 按最低团队人口排序，然后按团队ID排序
		int32 BestTeamId = INDEX_NONE; // 最佳团队ID
		uint32 BestPlayerCount = TNumericLimits<uint32>::Max(); // 最佳玩家计数
		for (const auto& KVP : TeamMemberCounts) // 遍历团队成员计数
		{
			const int32 TestTeamId = KVP.Key; // 测试团队ID
			const uint32 TestTeamPlayerCount = KVP.Value; // 测试团队玩家计数

			if (TestTeamPlayerCount < BestPlayerCount) // 如果玩家计数更少
			{
				BestTeamId = TestTeamId; // 更新最佳团队ID
				BestPlayerCount = TestTeamPlayerCount; // 更新最佳玩家计数
			}
			else if (TestTeamPlayerCount == BestPlayerCount) // 如果玩家计数相同
			{
				if ((TestTeamId < BestTeamId) || (BestTeamId == INDEX_NONE)) // 如果团队ID更小或最佳团队ID无效
				{
					BestTeamId = TestTeamId; // 更新最佳团队ID
					BestPlayerCount = TestTeamPlayerCount; // 更新最佳玩家计数
				}
			}
		}

		return BestTeamId; // 返回最佳团队ID
	}

	return INDEX_NONE; // 返回无效团队ID
}
#endif	// WITH_SERVER_CODE